home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power Programmierung
/
Power-Programmierung (Tewi)(1994).iso
/
magazine
/
msysjour
/
vol06
/
03
/
lanman3
/
wndproc.c
< prev
Wrap
C/C++ Source or Header
|
1991-05-01
|
18KB
|
703 lines
//===================================================================
// WndProc.c
//===================================================================
#include "phone.h"
#include "runtime.h"
#define ERROR_PIPE_BROKEN 109
#define ERROR_INVALID_HANDLE 6
#define TIMER_TICK 50
static char szEditClass[] = "edit";
static BOOL timerSet = FALSE;
static BOOL exitFlag = FALSE;
FARPROC lpfnLocalChild; //... pointer to local window subclass
FARPROC LocalProcInst; //... local procedure instance
FARPROC lpfnConfigDialogProc; //... pointer to Config subclass
FARPROC lpfnCallDialogProc; //... pointer to Call subclass
FARPROC lpfnAboutDialogProc; //... pointer to About subclass
char format[80]; //... used for wsprintf()
BOOL config = FALSE; //... indicates proper configuration
STATE state = START_STATE; //... state variable
HWND hLocalChild; //... handle to local window
HWND hRemoteChild; //... handle to remote window
int PipeHandle = -1; //... this is the handle of THE pipe.
//... Name buffers
char szClientName[NAMESIZE] = "";
char szRemoteName[NAMESIZE] = "";
char szPbxName[NAMESIZE];
char szPipeName[RMLEN+1];
WORD ConnectToPbx(void);
long FAR PASCAL WndProc(hWnd, iMessage, wParam, lParam)
HWND hWnd;
unsigned iMessage;
WORD wParam;
LONG lParam;
{
short Row, Center;
static short Width, Height;
WORD nBytes;
WORD rc;
HDC hDC;
PAINTSTRUCT ps;
TEXTMETRIC tm;
RECT rect;
static HWND hInstance;
switch(iMessage)
{
case WM_CREATE:
//=======================================================
// Set up Local Edit Control
//=======================================================
hInstance = ((LPCREATESTRUCT) lParam)->hInstance;
hLocalChild = CreateWindow(szEditClass,
NULL, EDITSTYLE,
0, 0, 0, 0,
hWnd, LOCAL_ID,
hInstance, NULL);
lpfnLocalChild = (FARPROC) GetWindowLong(hLocalChild,
GWL_WNDPROC);
LocalProcInst = MakeProcInstance(
(FARPROC) LocalProc,
GetWindowWord(hWnd, GWW_HINSTANCE));
SetWindowLong(hLocalChild,
GWL_WNDPROC,
(LONG) LocalProcInst);
//=======================================================
// Set up Remote Edit Control
//=======================================================
hRemoteChild = CreateWindow(szEditClass,
NULL, EDITSTYLE,
0, 0, 0, 0, hWnd,
REMOTE_ID,
hInstance,
NULL);
//=======================================================
// Set Dialog procedure pointers
//=======================================================
lpfnConfigDialogProc = MakeProcInstance(ConfigDialogProc,
hInstance);
lpfnCallDialogProc = MakeProcInstance(CallDialogProc,
hInstance);
lpfnAboutDialogProc = MakeProcInstance(AboutDialogProc,
hInstance);
if ( PbxSearch(szPbxName) == NERR_Success )
{
strcpy(szPipeName, szPbxName);
strcat(szPipeName, PIPELINE);
state = IDLE_STATE;
}
else
{
PostMessage(hWnd, WM_DESTROY, 0, 0L);
}
break;
case WM_SETFOCUS:
SetFocus(hLocalChild);
break;
case WM_PAINT:
hDC = BeginPaint(hWnd, &ps);
SetBkColor(hDC, RGB(0, 0, 255));
GetTextMetrics(hDC, &tm);
SetTextColor(hDC, RGB(255, 255, 255));
Row = tm.tmHeight + tm.tmExternalLeading;
rect.top = 0;
rect.left = 0;
rect.right = Width;
rect.bottom = Row;
Center = (Width - 5 * tm.tmAveCharWidth) / 2;
ExtTextOut(hDC, Center, 0,
ETO_OPAQUE, &rect,
"LOCAL", 5, NULL);
rect.left = 0;
rect.top = Height;
rect.right = Width;
rect.bottom = Height+Row;
Center = (Width - 6 * tm.tmAveCharWidth) / 2;
ExtTextOut(hDC, Center, Height,
ETO_OPAQUE, &rect,
"REMOTE", 6, NULL);
EndPaint(hWnd, &ps);
break;
case WM_SIZE:
Width = LOWORD(lParam);
Height = HIWORD(lParam) >> 1;
hDC = BeginPaint(hWnd, &ps);
GetTextMetrics(hDC, &tm);
Row = tm.tmHeight + tm.tmExternalLeading;
MoveWindow(hLocalChild, 0, Row, Width,
Height-Row, TRUE);
MoveWindow(hRemoteChild, 0, Row+Height,
Width, Height-Row, TRUE);
EndPaint(hWnd, &ps);
InvalidateRect(hWnd, NULL, TRUE);
break;
//===========================================================
// The following cases apply to menu options. (e.g. CALL)
//===========================================================
case WM_COMMAND:
switch(wParam)
{
case IDM_CALL:
if ( !config )
{
DisplayMsgBox(hParent,
"You are not configured!");
}
else
{
if ( state == IDLE_STATE )
{
if ( DialogBox(hInstance,
"CallBox",
hWnd,
lpfnCallDialogProc) )
{
InvalidateRect(hWnd, NULL, TRUE);
}
}
else
{
DisplayMsgBox(hWnd,
"You must hangup before making call.");
}
}
break;
case IDM_CONFIG:
if ( DialogBox(hInstance,
"ConfigBox",
hWnd,
lpfnConfigDialogProc) )
{
InvalidateRect(hWnd, NULL, TRUE);
}
if ( !config && strlen(szClientName) )
{
config = TRUE;
ConnectToPbx();
}
break;
case IDM_HANGUP:
if ( state == TALK_STATE )
{
HANGUP hangup;
hangup.wPktId = HANGUP_ID;
hangup.wPktSize = sizeof(HANGUP);
hangup.wRetCode = 0;
_lwrite(PipeHandle,
(LPSTR) &hangup,
sizeof(HANGUP));
ClearEditWindow(hLocalChild);
ClearEditWindow(hRemoteChild);
state = HANGUP_STATE;
}
else
{
DisplayMsgBox(hWnd, "You are not connected!");
}
break;
case IDM_EXIT:
exitFlag = TRUE;
SendMessage(hWnd, WM_COMMAND, IDM_HANGUP, 0L);
break;
case IDM_ABOUT:
DialogBox(hInstance,
"AboutBox",
hWnd,
lpfnAboutDialogProc);
break;
default:
break;
}
break;
//===========================================================
// All incoming data from PBX is read here. Since data can
// arrive asynchronously from PBX or the other client and
// since there is no facility for notifying this application
// of available data, we read the pipe on discrete time
// intervals. For each timer message we peek the pipe to
// see if data is available. If there data exists, we
// switch to the appropriate case label and satisfy the
// next request a in first come first serve order.
//===========================================================
case WM_TIMER:
//=======================================================
// At any time we may receive a hangup request and we
// must handle it immediately.
//=======================================================
if ( state == HANGUP_STATE )
{
WORD fState, err;
//... check if the handle is valid
err = DosQNmPHandState(PipeHandle, &fState);
//... if so, the pipe is disconnected
if ( err == ERROR_PIPE_BROKEN ||
err == ERROR_INVALID_HANDLE )
{
state = IDLE_STATE; //... enter idle state
if ( timerSet ) //... turn off the timer
{
KillTimer(hWnd, 1);
timerSet = FALSE;
}
//... If the exit flag is set then the hangup
//... was sent as a result of a user exit so
//... we must post a quit message and exit the
//... program. Otherwise, we just hungup so we
//... need ro re-connect to the PBX.
if ( exitFlag )
PostMessage(hWnd, WM_DESTROY, 0, 0L);
else
ConnectToPbx();
}
}
else
{
//===================================================
// The incoming data can take a variety of forms
// so we use a union to save space while at the
// same time you can use convient struct notation
// instead of fancy casting.
//===================================================
union {
HEADER header; //... header message
PBXPKT pbx; //... pbx message
CHRMSG chrmsg; //... char message
HANGUP hangup; //... hangup message
} packet;
WORD nBytesRead, nBytesAvail, err;
//===================================================
// PBX uses a byte-mode pipe and its own message
// headers. Each message retrieved (including
// phone specific) has the same header with an
// opcode identifying the type of message. Since
// we do not know how many bytes there are to read
// and we must read them all, we shall do a peek
// on the pipe. The number of bytes we will peek
// is the size of the common header.
//===================================================
err = PeekPipe(PipeHandle, &packet, sizeof(HEADER),
&nBytesRead, &nBytesAvail);
if ( err || nBytesAvail == 0 )
{
break; //... failure peeking pipe or no data
}
//===================================================
// At this point, we must switch on the message
// type, disregarding the ACK bit. The independent
// messages will take care of the ACK if one is
// received.
//===================================================
switch( (int) (packet.header.wPktId & ~ACK) )
{
case LISTQUERY:
//===============================================
// Obtain the list of users at the PBX.
//===============================================
if (state != START_STATE )
{
ReadListQuery();
}
break;
case REGISTER:
//===============================================
// PBX will send back ACK for each register
// sent to it. The ACK will contain a return
// code which is not checked here inorder to
// keep this code simple, but should be.
//===============================================
if ( (packet.header.wPktId & ACK) == ACK )
{
_lread(PipeHandle,
(LPSTR) &packet,
sizeof(PBXPKT));
}
break;
case CHRMSG_ID:
//===============================================
// Once we are connected to another client,
// the dialog will consist of messages containing
// each character typed. The message is read
// and sent to the remote child window using
// the PostMessage() function.
//===============================================
if ( state == TALK_STATE )
{
do {
_lread(PipeHandle,
(LPSTR) &packet,
sizeof(CHRMSG));
SendMessage(hRemoteChild,
WM_CHAR,
packet.chrmsg.wParam,
packet.chrmsg.lParam);
err = PeekPipe(PipeHandle,
&packet,
sizeof(HEADER),
&nBytesRead,
&nBytesAvail);
if (err || nBytesRead == 0)
{
break;
}
}
while(packet.header.wPktId==CHRMSG_ID);
}
break;
case HANGUP_ID:
//===========================================
// During a dialog either client can hangup.
// When a client hangups up, a hangup
// message is sent to the remote client and
// is processed here. PBX does not notify
// the remote client that its pipe is being
// closed, therefore, the remote client will
// not know until its next pipe operation
// fails; this is most undesirable. This
// hangup message is just a nice way of
// telling the client to disconnect.
//===========================================
if ( (packet.header.wPktId & ACK) == 0 )
{
state = HANGUP_STATE;
_lread(PipeHandle,
(LPSTR) &packet,
sizeof(HANGUP));
_lclose(PipeHandle);
ClearEditWindow(hLocalChild);
ClearEditWindow(hRemoteChild);
strcpy(format, szRemoteName);
strcat(format, " HAS HUNGUP");
DisplayMsgBox(hWnd, format);
}
break;
case CONNECT:
//==========================================
// The most complex message to process is
// the CONNECT message. The following
// sequence illustrates the handshacking
// sequence.
//
// CALLER CALLEE
// ______ ------
// Send Connect to PBX Recv connect
// Recv ACK from PBX Send ACK to caller
// Recv ACK from callee
//==========================================
if ( (packet.header.wPktId & ACK) == ACK )
{
switch(state)
{
case CALL_STATE:
//==================================
// Receive ACK from PBX here and
// wait state, waiting for callee
// ACK.
//==================================
err = _lread(PipeHandle,
(LPSTR) &packet,
sizeof(PBXPKT));
state = ( (int) err > 0 ?
WAIT_STATE :
IDLE_STATE );
break;
case WAIT_STATE:
//==================================
// Receive ACK from callee and
// enter the talk state.
//==================================
_lread(PipeHandle,
(LPSTR) &packet,
sizeof(PBXPKT));
if ( packet.pbx.usRetCode )
{
state = HANGUP_STATE;
_lclose(PipeHandle);
DisplayMsgBox(hWnd,
"CALL REFUSED!");
}
else
{
state = TALK_STATE;
DisplayMsgBox(hWnd,
"YOU MAY TYPE");
}
break;
default: break;
}
}
else
{
//======================================
// Receive CONNECT from PBX signaling
// that we are being called. To keep
// this sample program simple, the
// callee does not get the option to
// to refuse the call.
//======================================
if ( state == IDLE_STATE )
{
_lread(PipeHandle,
(LPSTR) &packet,
sizeof(PBXPKT));
strcpy(szRemoteName,
packet.pbx.aPBXName[0].pszName);
strcpy(format, szRemoteName);
strcat(format, " IS CALLING YOU");
rc = MessageBox(hWnd,
format,
PROGNAME,
MB_ICONEXCLAMATION|MB_YESNO);
packet.pbx.usPktID = (CONNECT | ACK);
packet.pbx.usPktSize = sizeof(PBXPKT);
if ( rc == IDYES )
{
state = TALK_STATE;
packet.pbx.usRetCode = 0;
}
else
{
state = HANGUP_STATE;
packet.pbx.usRetCode = -1;
}
_lwrite(PipeHandle,
(LPSTR) &packet,
sizeof(PBXPKT));
}
}
break;
}
}
break;
case WM_DESTROY:
if ( state == START_STATE )
{
DisplayMsgBox(hWnd,
"Initialization failed. Program will terminate.");
}
else if ( state != TALK_STATE )
{
_lclose(PipeHandle);
}
FreeProcInstance(lpfnConfigDialogProc);
FreeProcInstance(lpfnCallDialogProc);
FreeProcInstance(lpfnAboutDialogProc);
PostQuitMessage(0); // quit program
break;
default:
return DefWindowProc(hWnd, iMessage, wParam, lParam);
}
return 0L;
}
//===================================================================
// ConnectToPbx()
//
// This procedure performs the OpenPipe, PbxRegister, start timer
// and SubmitListQuery sequence.
//
//===================================================================
WORD ConnectToPbx(void)
{
WORD err;
err = OpenPipe(&PipeHandle, szPipeName);
if ( err )
{
wsprintf(format,
"Error opening pipe: %u", err);
DisplayMsgBox(hParent, format);
}
else
{
err = PbxRegister(PipeHandle, szClientName, PHONE_ID);
if ( err )
{
wsprintf(format, "PBX Register error %u", err);
DisplayMsgBox(hParent, format);
}
else
{
if ( !timerSet )
{
SetTimer(hParent, 1, TIMER_TICK, NULL);
timerSet = TRUE;
}
if ( SubmitListQuery() )
{
DisplayMsgBox(hParent, "SubmitListQuery failed");
}
}
}
return err;
}
//===================================================================
// LocalProc -- Controls local window
//
// This procesdure it the entry point for all messages being sent
// to the LOCAL window.
//===================================================================
long FAR PASCAL LocalProc(hWnd, iMessage, wParam, lParam)
HWND hWnd;
unsigned iMessage;
WORD wParam;
LONG lParam;
{
int nBytes, rc;
WORD chr;
if ( iMessage != WM_CHAR )
{
return CallWindowProc(lpfnLocalChild, hWnd,
iMessage, wParam, lParam);
}
if ( state != TALK_STATE )
{
if ( iMessage == WM_KEYDOWN )
{
DisplayMsgBox(hParent,
"Not currently connected!");
}
return CallWindowProc(lpfnLocalChild, hWnd,
WM_CLEAR, wParam, lParam);
}
else
{
CHRMSG chrmsg;
chrmsg.header.wPktId = CHRMSG_ID;
chrmsg.header.wPktSize = sizeof(CHRMSG);
chrmsg.wParam = wParam;
chrmsg.lParam = lParam;
_lwrite(PipeHandle, (LPSTR) &chrmsg, sizeof(CHRMSG));
}
return CallWindowProc(lpfnLocalChild, hWnd,
iMessage, wParam, lParam);
}
//===================================================================
// ClearEditWindow() clears the child windows after
// a conversation has terminated.
//===================================================================
void FAR PASCAL ClearEditWindow(hWnd)
HWND hWnd;
{
SendMessage(hWnd, EM_SETSEL, 0, MAKELONG(0, 32767));
SendMessage(hWnd, EM_REPLACESEL, 0, (LONG) (LPVOID) "");
}
//===================================================================
// DisplayMsgBox() is used for alerting the user.
//===================================================================
void FAR PASCAL DisplayMsgBox(HWND hWnd, char *str)
{
MessageBox(hWnd, str, PROGNAME, MB_ICONEXCLAMATION | MB_OK);
}